#include "charge_point.h"
#include <optional>

namespace application {
namespace charge {

ChargePoint::ChargePoint(const QString& pointId, int floor, const kernel::Position& position,
                         std::shared_ptr<kernel::ILogger> logger, QObject* parent)
    : QObject(parent)
    , m_pointId(pointId)
    , m_floor(floor)
    , m_position(position)
    , m_stationPoint(pointId)
    , m_logger(logger)
{
}

bool ChargePoint::occupy(int agvId) {
    if (m_isOccupied) {
        if (m_logger) {
            m_logger->warning(QString("充电点 %1 已被占用 (AGV: %2)")
                .arg(m_pointId).arg(m_currentOccupyingAgvId));
        }
        return false;
    }

    m_isOccupied = true;
    m_currentOccupyingAgvId = agvId;

    if (m_logger) {
        m_logger->info(QString("充电点 %1 被 AGV %2 占用").arg(m_pointId).arg(agvId));
    }

    emit occupationChanged(true, agvId);
    return true;
}

bool ChargePoint::releaseOccupation() {
    if (!m_isOccupied) {
        return false;
    }

    int previousAgvId = m_currentOccupyingAgvId;
    m_isOccupied = false;
    m_currentOccupyingAgvId = -1;

    if (m_logger) {
        m_logger->info(QString("充电点 %1 释放占用 (之前 AGV: %2)").arg(m_pointId).arg(previousAgvId));
    }

    emit occupationChanged(false, previousAgvId);
    return true;
}

bool ChargePoint::preOccupy(int agvId) {
    if (m_preOccupyingAgvId != -1 && m_preOccupyingAgvId != agvId) {
        if (m_logger) {
            m_logger->warning(QString("充电点 %1 已被预占用 (AGV: %2)")
                .arg(m_pointId).arg(m_preOccupyingAgvId));
        }
        return false;
    }

    m_preOccupyingAgvId = agvId;

    if (m_logger) {
        m_logger->debug(QString("充电点 %1 被 AGV %2 预占用").arg(m_pointId).arg(agvId));
    }

    emit preOccupationChanged(true, agvId);
    return true;
}

bool ChargePoint::releasePreOccupation(int agvId) {
    if (m_preOccupyingAgvId != agvId) {
        return false;
    }

    m_preOccupyingAgvId = -1;

    if (m_logger) {
        m_logger->debug(QString("充电点 %1 释放预占用 (AGV: %2)").arg(m_pointId).arg(agvId));
    }

    emit preOccupationChanged(false, agvId);
    return true;
}

namespace {

std::optional<kernel::agv::AgvType> agvTypeFromString(const QString& value)
{
    const QString type = value.trimmed().toUpper();
    if (type == QLatin1String("SEER")) {
        return kernel::agv::AgvType::SEER;
    }
    if (type == QLatin1String("MKLM")) {
        return kernel::agv::AgvType::MKLM;
    }
    return std::nullopt;
}

} // namespace

void ChargePoint::setArea(std::shared_ptr<area::Area> area) {
    if (m_area) {
        disconnect(m_area.get(), nullptr, this, nullptr);
    }

    m_area = area;
    m_areaId = area ? area->getAreaId() : QString();

    if (m_area) {
        connect(m_area.get(), &area::Area::areaEntered, this, &ChargePoint::onAreaEntered);
        connect(m_area.get(), &area::Area::areaExited, this, &ChargePoint::onAreaExited);
    }
}

void ChargePoint::setStationPoint(const QString& stationPoint) {
    m_stationPoint = stationPoint;
}

void ChargePoint::setActionPoint(const QString& actionPoint) {
    m_actionPoint = actionPoint;
}

void ChargePoint::setAllowedVehicles(const QList<int>& allowedVehicles) {
    m_allowedVehicles.clear();
    for (int number : allowedVehicles) {
        m_allowedVehicles.insert(number);
    }
}

bool ChargePoint::isVehicleAllowed(int agvId) const {
    if (m_blockedVehicles.contains(agvId)) {
        return false;
    }
    if (m_allowedVehicles.isEmpty()) {
        return true;
    }
    return m_allowedVehicles.contains(agvId);
}

void ChargePoint::setSupportedAgvTypes(const QList<QString>& types)
{
    m_supportedAgvTypes.clear();
    for (const auto& typeStr : types) {
        if (auto type = agvTypeFromString(typeStr)) {
            m_supportedAgvTypes.insert(*type);
        }
    }
}

bool ChargePoint::supportsAgvType(kernel::agv::AgvType type) const
{
    if (m_supportedAgvTypes.isEmpty()) {
        return true;
    }
    return m_supportedAgvTypes.contains(type);
}

void ChargePoint::setBlockedVehicles(const QList<int>& blockedVehicles) {
    m_blockedVehicles.clear();
    for (int number : blockedVehicles) {
        m_blockedVehicles.insert(number);
    }
}

void ChargePoint::onAreaEntered(int agvId, const QString& areaId) {
    Q_UNUSED(areaId);
    occupy(agvId);
}

void ChargePoint::onAreaExited(int agvId, const QString& areaId) {
    Q_UNUSED(areaId);
    if (m_currentOccupyingAgvId == agvId) {
        releaseOccupation();
    }
}

} // namespace charge
} // namespace application
